package com.quicks.beauty.server.common;

import com.alibaba.fastjson.JSON;
import com.quicks.beauty.server.config.WXConfig;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.XmlFriendlyNameCoder;
import com.thoughtworks.xstream.io.xml.XppDriver;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHeaders;
import org.apache.http.StatusLine;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.methods.RequestBuilder;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.message.BasicHeader;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import weixin.popular.client.LocalHttpClient;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.nio.charset.Charset;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;

@Component
public class WeiXinUtils {

	private static Logger logger = LoggerFactory.getLogger(WeiXinUtils.class);

	protected static Header xmlHeader = new BasicHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_XML.toString());
	protected static final String MCH_URI = "https://api.mch.weixin.qq.com";

	@Autowired
    WXConfig wxConfig;

	/**
	 * 返回小程序发起支付参数
	 */
	public RepeatRequest preparePayToGenerateXMLResponse(String xmlRequestStr) {
		RepeatRequest repeatRequest = new RepeatRequest();

		HttpUriRequest httpUriRequest = RequestBuilder.post().setHeader(xmlHeader).setUri(MCH_URI + "/pay/unifiedorder")
				.setCharset(Charset.forName("utf-8")).setEntity(new StringEntity(xmlRequestStr, Charset.forName("utf-8")))
				.build();
		CloseableHttpResponse response = LocalHttpClient.execute(httpUriRequest);
		StatusLine statusLine = response.getStatusLine();
		int statusCode = statusLine.getStatusCode();
		try {
			if (statusCode >= 200 && statusCode < 300) {
				HttpEntity entity = response.getEntity();
				String str = EntityUtils.toString(entity);
				Header contentType = response.getEntity().getContentType();

				if (contentType != null && !contentType.toString().matches(".*[uU][tT][fF]-8$")) {
					str = new String(str.getBytes("iso-8859-1"), "utf-8");
				}

				XStream xStream = new XStream(new XppDriver(new XmlFriendlyNameCoder("_-", "_")));// 说明3(见文末)
				xStream.alias("xml", UnifiedOrderResponse.class);
				Object obj = xStream.fromXML(str);
				UnifiedOrderResponse unifiedOrderResponse = new UnifiedOrderResponse();
				BeanUtils.copyProperties(obj, unifiedOrderResponse);
				logger.info(String.format("orginal text is :%s", JSON.toJSONString(unifiedOrderResponse)));
				if (null != unifiedOrderResponse && "SUCCESS".equals(unifiedOrderResponse.getReturn_code())
						&& "SUCCESS".equals(unifiedOrderResponse.getResult_code())) {
					/**
					 * 构建二次校验参数
					 */
					repeatRequest.setPrepay_id(unifiedOrderResponse.getPrepay_id());
					repeatRequest.setAppId(unifiedOrderResponse.getAppid());
					repeatRequest.setTimeStamp(System.currentTimeMillis() / 1000);
					repeatRequest.setFlag(Boolean.TRUE);
					repeatRequest.setNonceStr(WeiXinUtils.genaratorRandomString(32));
					String sign = returnPaySign(repeatRequest, wxConfig.getApiKey());
					repeatRequest.setPaySign(sign);
					return repeatRequest;
				}
			} else {
				repeatRequest.setFlag(Boolean.FALSE);
				return repeatRequest;
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return repeatRequest;
	}

	public OrderQueryResponse getOrderQuery(String queryXml) {
		OrderQueryResponse oqres = new OrderQueryResponse();
		HttpUriRequest httpUriRequest = RequestBuilder.post().setHeader(xmlHeader).setUri(MCH_URI + "/pay/orderquery")
				.setCharset(Charset.forName("utf-8")).setEntity(new StringEntity(queryXml, Charset.forName("utf-8"))).build();
		CloseableHttpResponse response = LocalHttpClient.execute(httpUriRequest);
		StatusLine statusLine = response.getStatusLine();
		int statusCode = statusLine.getStatusCode();
		try {
			if (statusCode >= 200 && statusCode < 300) {
				HttpEntity entity = response.getEntity();
				String str = EntityUtils.toString(entity);
				Header contentType = response.getEntity().getContentType();

				if (contentType != null && !contentType.toString().matches(".*[uU][tT][fF]-8$")) {
					str = new String(str.getBytes("iso-8859-1"), "utf-8");
				}

				XStream xStream = new XStream(new XppDriver(new XmlFriendlyNameCoder("_-", "_")));// 说明3(见文末)
				xStream.alias("xml", OrderQueryResponse.class);
				Object obj = xStream.fromXML(str);
				oqres = new OrderQueryResponse();
				BeanUtils.copyProperties(obj, oqres);
				logger.info(String.format("orginal text is :%s", JSON.toJSONString(oqres)));
				/**
				 * 构建二次校验参数
				 */
				return oqres;
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}

	public String getXmlRequest(String nonce, String openId, BigDecimal payMoney, String orderNo, String productName) {
		/**
		 * paySign = MD5(appId=wxd678efh567hg6787&nonceStr=
		 * 5K8264ILTKCH16CQ2502SI8ZNMTM67VS&package=prepay_id=
		 * wx2017033010242291fcfe0db70013231072&signType=MD5&timeStamp=
		 * 1490840662&key=qazwsxedcrfvtgbyhnujmikolp111111) =
		 * 22D9B4E54AB1950F51E0649E8810ACD6
		 */
		String appid = wxConfig.getAppId();
		String merchantId = wxConfig.getMerchantId();
		String invokeUrl = wxConfig.getServerUrl();
		String clientIP = wxConfig.getClientIp();
		String apiKey = wxConfig.getApiKey();

		UnifiedOrderRequest orderRequest = new UnifiedOrderRequest();

		DecimalFormat df = new DecimalFormat("#");
		String money = df.format(payMoney.multiply(new BigDecimal(100)));
		orderRequest.setAppid(appid);
		orderRequest.setBody(productName);
		orderRequest.setMch_id(merchantId);
		orderRequest.setNonce_str(nonce);
		orderRequest.setNotify_url(invokeUrl);
		orderRequest.setOut_trade_no(orderNo);
		orderRequest.setSpbill_create_ip(clientIP);
		orderRequest.setTrade_type("JSAPI"); // JSAPI--公众号支付、NATIVE--原生扫码支付、APP--app支付
		orderRequest.setTotal_fee(Integer.parseInt(money)); // 单位是:分
		orderRequest.setOpenid(openId);
		orderRequest.setSign(createSign(orderRequest, apiKey).toUpperCase());// 签名

		XStream xStream = new XStream(new XppDriver(new XmlFriendlyNameCoder("_-", "_")));
		xStream.alias("xml", UnifiedOrderRequest.class);// 根元素名需要是xml
		String xmlRequest = xStream.toXML(orderRequest);
		return xmlRequest;
	}

    public String generateXmlRequest(String nonce, String openId, BigDecimal payMoney, String orderNo, String productName) {
        /**
         * paySign = MD5(appId=wxd678efh567hg6787&nonceStr=
         * 5K8264ILTKCH16CQ2502SI8ZNMTM67VS&package=prepay_id=
         * wx2017033010242291fcfe0db70013231072&signType=MD5&timeStamp=
         * 1490840662&key=qazwsxedcrfvtgbyhnujmikolp111111) =
         * 22D9B4E54AB1950F51E0649E8810ACD6
         */
        String appid = wxConfig.getAppId();
        String merchantId = wxConfig.getMerchantId();
        String invokeUrl = wxConfig.getServerUrl();
        String clientIP = wxConfig.getClientIp();
        String apiKey = wxConfig.getApiKey();

        UnifiedOrderRequest orderRequest = new UnifiedOrderRequest();

        DecimalFormat df = new DecimalFormat("#");
        String money = df.format(payMoney.multiply(new BigDecimal(100)));
        orderRequest.setAppid(appid);
        orderRequest.setBody(productName);
        orderRequest.setMch_id(merchantId);
        orderRequest.setNonce_str(nonce);
        orderRequest.setNotify_url(invokeUrl+"/"+orderNo);
        orderRequest.setOut_trade_no(orderNo);
        orderRequest.setSpbill_create_ip(clientIP);
        orderRequest.setTrade_type("JSAPI"); // JSAPI--公众号支付、NATIVE--原生扫码支付、APP--app支付
        orderRequest.setTotal_fee(Integer.parseInt(money)); // 单位是:分
        orderRequest.setOpenid(openId);
        orderRequest.setSign(createSign(orderRequest, apiKey).toUpperCase());// 签名

        XStream xStream = new XStream(new XppDriver(new XmlFriendlyNameCoder("_-", "_")));
        xStream.alias("xml", UnifiedOrderRequest.class);// 根元素名需要是xml
        String xmlRequest = xStream.toXML(orderRequest);
        return xmlRequest;
    }



    public String getXmlRequest(String orderNo) {
		/**
		 * paySign = MD5(appId=wxd678efh567hg6787&nonceStr=
		 * 5K8264ILTKCH16CQ2502SI8ZNMTM67VS&package=prepay_id=
		 * wx2017033010242291fcfe0db70013231072&signType=MD5&timeStamp=
		 * 1490840662&key=qazwsxedcrfvtgbyhnujmikolp111111) =
		 * 22D9B4E54AB1950F51E0649E8810ACD6
		 */
		String appid = wxConfig.getAppId();
		String merchantId = wxConfig.getMerchantId();
		String apiKey = wxConfig.getApiKey();

		UnifiedOrderRequest orderRequest = new UnifiedOrderRequest();

		orderRequest.setAppid(appid);
		orderRequest.setMch_id(merchantId);
		orderRequest.setNonce_str(genaratorRandomString(32));
		orderRequest.setOut_trade_no(orderNo);
		orderRequest.setSign(createSign(orderRequest, apiKey).toUpperCase());// 签名
		XStream xStream = new XStream(new XppDriver(new XmlFriendlyNameCoder("_-", "_")));
		xStream.alias("xml", UnifiedOrderRequest.class);// 根元素名需要是xml
		String xmlRequest = xStream.toXML(orderRequest);
		return xmlRequest;
	}

	private static String createSign(UnifiedOrderRequest unifiedOrderRequest, String apiKey) {
		// 根据规则创建可排序的map集合
		HashMap<String, String> packageParams = new HashMap<>();
		packageParams.put("appid", unifiedOrderRequest.getAppid());
		packageParams.put("body", unifiedOrderRequest.getBody());
		packageParams.put("mch_id", unifiedOrderRequest.getMch_id());
		packageParams.put("nonce_str", unifiedOrderRequest.getNonce_str());
		packageParams.put("notify_url", unifiedOrderRequest.getNotify_url());
		packageParams.put("out_trade_no", unifiedOrderRequest.getOut_trade_no());
		packageParams.put("spbill_create_ip", unifiedOrderRequest.getSpbill_create_ip());
		packageParams.put("trade_type", unifiedOrderRequest.getTrade_type());
		packageParams.put("total_fee",
				unifiedOrderRequest.getTotal_fee() == null ? null : String.valueOf(unifiedOrderRequest.getTotal_fee()));
		packageParams.put("openid", unifiedOrderRequest.getOpenid());
		String presign = createLinkString(packageParams);
		String sign = sign(presign, apiKey, "utf-8");
		return sign;
	}

	public String returnPaySign(RepeatRequest repeatRequest, String key) {
		HashMap<String, String> packageParams = new HashMap<>();
		packageParams.put("appId", repeatRequest.getAppId());
		packageParams.put("timeStamp", repeatRequest.getTimeStamp() + "");
		packageParams.put("nonceStr", repeatRequest.getNonceStr());
		packageParams.put("package", "prepay_id=" + repeatRequest.getPrepay_id());
		packageParams.put("signType", "MD5");
		String returnsign = createLinkString(packageParams);
		return sign(returnsign, key, "utf-8");
	}

	public static String sign(String text, String key, String input_charset) {
		text = text + "&key=" + key;
		return DigestUtils.md5Hex(getContentBytes(text, input_charset));
	}

	public static byte[] getContentBytes(String content, String charset) {
		if (charset == null || "".equals(charset)) {
			return content.getBytes();
		}
		try {
			return content.getBytes(charset);
		} catch (UnsupportedEncodingException e) {
			throw new RuntimeException("MD5签名过程中出现错误,指定的编码集不对,您目前指定的编码集是:" + charset);
		}
	}

	public static String createLinkString(Map<String, String> params) {
		MapUtil.removeNullValue(params);
		List<String> keys = new ArrayList<>(params.keySet());
		Collections.sort(keys);
		String prestr = "";
		for (int i = 0; i < keys.size(); i++) {
			String key = keys.get(i);
			String value = params.get(key);
			if (i == keys.size() - 1) {// 拼接时，不包括最后一个&字符
				prestr = prestr + key + "=" + value;
			} else {
				prestr = prestr + key + "=" + value + "&";
			}
		}
		return prestr;
	}

	public static String genaratorRandomString(int length) {
		String base = "abcdefghijklmnopqrstuvwxyz0123456789";
		Random random = new Random();
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < length; i++) {
			int number = random.nextInt(base.length());
			sb.append(base.charAt(number));
		}
		return sb.toString();
	}

}
